package com.amt.util;

import com.amt.exception.InterfaceParamException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @Title: RSAUtil.java
 * @Package com.amt.util
 * @Description: TODO(RSA算法工具类)
 * @author 江伟
 * @date 2020年3月13日 下午4:52:04 Copyright (c) ©1994-2021 Scjydz.com All Rights
 *       Reserved.
 */
@Slf4j
@Component
public class RSAUtil {

	private static Map<Integer, String> keyMap = new HashMap<Integer, String>(); // 用于封装随机产生的公钥与私钥

	/**
	 * RSA密钥长度必须是64的倍数，在512~65536之间。默认是1024
	 */
	public static final int KEY_SIZE = 1024;
	
	/**
	 * 随机生成密钥对
	 * 
	 * @throws NoSuchAlgorithmException
	 */
	public static void genKeyPair() throws NoSuchAlgorithmException {
		// KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
		// 初始化密钥对生成器，密钥大小为96-1024位
		keyPairGen.initialize(1024, new SecureRandom());
		// 生成一个密钥对，保存在keyPair中
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥
		String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
		// 得到私钥字符串
		String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
		// 将公钥和私钥保存到Map
		keyMap.put(0, publicKeyString); // 0表示公钥
		keyMap.put(1, privateKeyString); // 1表示私钥
	}

	/**
	 * @description: 获取公钥对象
	 * @param publicKeyBase64 公钥base64 编码
	 * @return publicKeyBase64
	 * @throws InvalidKeySpecException
	 * @throws NoSuchAlgorithmException
	 */
	public PublicKey getPublicKey(String publicKeyBase64)
			throws InvalidKeySpecException, NoSuchAlgorithmException {
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		X509EncodedKeySpec publicpkcs8KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyBase64));
		PublicKey publicKey = keyFactory.generatePublic(publicpkcs8KeySpec);
		return publicKey;
	}

	/**
	 * @description: 获取私钥对象
	 * @param privateKeyBase64 私钥base64 编码
	 * @return 私钥对象
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeySpecException
	 */
	public PrivateKey getPrivateKey(String privateKeyBase64)
			throws NoSuchAlgorithmException, InvalidKeySpecException {
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		PKCS8EncodedKeySpec privatekcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyBase64));
		PrivateKey privateKey = keyFactory.generatePrivate(privatekcs8KeySpec);
		return privateKey;
	}

	 
	/**
	 * @description: 公钥加密
	 * @param content 待加密内容
	 * @param publicKeyBase64 公钥 base64 编码
	 * @return 经过 base64 编码后的字符串
	 */
	public String encipher(String content, String publicKeyBase64) throws InterfaceParamException{
		return encipher(content, publicKeyBase64, KEY_SIZE / 8 - 11);
	}

	/**
	 * @description: 使用公钥加密（分段加密）
	 * @param content         待加密内容
	 * @param publicKeyBase64 公钥 base64 编码
	 * @param segmentSize     分段大小,一般小于 keySize/8（段小于等于0时，将不使用分段加密）
	 * @return 经过 base64 编码后的字符串
	 */
	public String encipher(String content, String publicKeyBase64, int segmentSize) {
		try {
			PublicKey publicKey = getPublicKey(publicKeyBase64);
			return encipher(content, publicKey, segmentSize);
		} catch (Exception e) {
			log.error("公钥加密（分段加密）异常{}",e);
			return null;
		}
	}

	/**
	 * @description: 分段加密
	 * @param ciphertext  密文
	 * @param key         加密秘钥
	 * @param segmentSize 分段大小，<=0 不分段
	 * @return
	 */
	public String encipher(String ciphertext, Key key, int segmentSize) {
		try {
			// 用公钥加密
			byte[] srcBytes = ciphertext.getBytes();
			// Cipher负责完成加密或解密工作，基于RSA
			Cipher cipher = Cipher.getInstance("RSA");
			// 根据公钥，对Cipher对象进行初始化
			cipher.init(Cipher.ENCRYPT_MODE, key);
			byte[] resultBytes = null;
			if (segmentSize > 0)
				resultBytes = cipherDoFinal(cipher, srcBytes, segmentSize); // 分段加密
			else
				resultBytes = cipher.doFinal(srcBytes);
			String base64Str = Base64Utils.encodeToString(resultBytes);
			return base64Str;
		} catch (Exception e) {
			log.error("分段加密异常{}",e);
			return null;
		}
	}

	/**
	 * @description: 分段大小
	 * @param cipher
	 * @param srcBytes
	 * @param segmentSize
	 * @return
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws IOException
	 */
	public byte[] cipherDoFinal(Cipher cipher, byte[] srcBytes, int segmentSize)
			throws IllegalBlockSizeException, BadPaddingException, IOException {
		if (segmentSize <= 0)
			throw new RuntimeException("分段大小必须大于0");
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int inputLen = srcBytes.length;
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > segmentSize) {
				cache = cipher.doFinal(srcBytes, offSet, segmentSize);
			} else {
				cache = cipher.doFinal(srcBytes, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * segmentSize;
		}
		byte[] data = out.toByteArray();
		out.close();
		return data;
	}

	/**
	 * @description: 使用私钥解密
	 * @param contentBase64    待加密内容,base64 编码
	 * @param privateKeyBase64 私钥 base64 编码
	 * @return
	 * @segmentSize 分段大小
	 */
	public String decipher(String contentBase64, String privateKeyBase64) throws InterfaceParamException{
		return decipher(contentBase64, privateKeyBase64, KEY_SIZE / 8);
	}

	/**
	 * @description: 使用私钥解密（分段解密）
	 * @param contentBase64    待加密内容,base64 编码
	 * @param privateKeyBase64 私钥 base64 编码
	 * @return
	 * @segmentSize 分段大小
	 */
	public String decipher(String contentBase64, String privateKeyBase64, int segmentSize) {
		try {
			PrivateKey privateKey = getPrivateKey(privateKeyBase64);
			return decipher(contentBase64, privateKey, segmentSize);
		} catch (Exception e) {
			log.error("私钥解密（分段解密）异常{}",e);
			return null;
		}
	}

	/**
     * @description: 分段解密
     * @param contentBase64 密文
     * @param key           解密秘钥
     * @param segmentSize   分段大小（小于等于0不分段）
     * @return
     */
    public String decipher(String contentBase64, Key key, int segmentSize) {
        try {
            // 用私钥解密
            byte[] srcBytes = Base64Utils.decodeFromString(contentBase64);
            // Cipher负责完成加密或解密工作，基于RSA
            Cipher deCipher = Cipher.getInstance("RSA");
            // 根据公钥，对Cipher对象进行初始化
            deCipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decBytes = null;
            if (segmentSize > 0)
                decBytes = cipherDoFinal(deCipher, srcBytes, segmentSize); //分段加密
            else
                decBytes = deCipher.doFinal(srcBytes);

            String decrytStr = new String(decBytes);
            return decrytStr;
        } catch (Exception e) {
        	log.error("分段解密异常{}",e);
            return null;
        }
    }

	public static void main(String[] args) throws Exception {
		// 生成公钥和私钥
		genKeyPair();

		System.out.println("随机生成的公钥为:" + keyMap.get(0));
		System.out.println("随机生成的私钥为:" + keyMap.get(1));
//		RSAUtil rsaUtil = new RSAUtil();
//		// 登录加密字符串
//		String message = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3Q4t160533KCLgPnFMu3GTWWCsQfztpABcy6Abh3jLIyTBcdOVdbvhPI31Iri37GqlJSVV3iv2ig3kXKiSgWmk9IGQB+Uah5I/XroWdvuyCTTYf1KGR+WgTrVDao5qgVhiKiVGYh+wPV8YWPvwssUhesHl2UtiGnSnmp9tMblDwIDAQAB";
//		String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCFc1+Dcpx+kFaoPOED9xhkk96TvsBiggw5OEVzcVnTPmtgfAK/bUZGJpjJdBRmO2ad5rCGOMDIVM3V1Y05efQE/wQN7LeAeWlbG+V6zc9b5eAmqL0ltuLI66GFTnSYPZPLRsFFL+vnjE2sTceIL2WKn9n7OYPOku0dy6z+e1nr8wIDAQAB";
//		String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAIVzX4NynH6QVqg84QP3GGST3pO+wGKCDDk4RXNxWdM+a2B8Ar9tRkYmmMl0FGY7Zp3msIY4wMhUzdXVjTl59AT/BA3st4B5aVsb5XrNz1vl4CaovSW24sjroYVOdJg9k8tGwUUv6+eMTaxNx4gvZYqf2fs5g86S7R3LrP57WevzAgMBAAECgYBTYxdAtMIS17A+aeQUrTbhygAfg+cE5swqiP3kt+Rv+iXwYSz6Xpa1yPJZlx4UD3P8oKSXCil0RIFl543aZ1jRSwrEmylDw47Q5jNbYMa3bkWD/rrQzX4Vr/sgtGvaM44wakzMbs3sG/lVQ4pJhp3L8CkYBS90WP8UoJdH3U8OgQJBAMNsyvUr8xU7D8xIFZJ+GIs/p6dJdH+mvErPRHbnZlWCel0XdfEfNYvQRSHQxiVa9aosSzwkeWzMBFLsPvynZbMCQQCu0NhedlkeUwlySIkh7HvBBL3RZH3op88zqTVvoah2FXrLPI5TyIHnjOR2ahBrZ4rmx/+UmkecpXsexW8HoMDBAkEAiKwrfS/Rf4Wg1NONErjRXYGVNO+wZB4uDXvLfAT1ktP/0982gv26qx9rWezyxulIak00CMnRif9c/slue7Bu9QJARPH7E65LUYlgRWFULDvvRt3O2yFuAu8D6hUHDSLNpNyt1C/q1iJTEXZguE72cNWiU4h3RS1fpukg2K73LiP5QQJBAL84L0LNpKcxj+EteKWK/yYV7LnzxHyQidMC9VnOniJivyvIGk26PGBSHfpvLEp2FgEUD3k2TWmp6lQ5osmt3no=";
//		String messageEn = rsaUtil.encipher(message, publicKey);
//		System.out.println("加密后的字符串为:" + messageEn);
//		String res = "NwOjhQPg89ePuJGAo/iHHMOSFYnf82SwUTpyXlf0XqKkXfpx60dI8Mgq2qWVgnv7KiHfbmdd0P2A2HPcATWXxAXJqA0hIDaHKY//haerXUGez/Yqfd3ILr6qnb9vo83FfPGdjXMBPZ6FtdJqug1zqfHdNnAUdJT7+PtaPj3zjTl6oaqhErfLl4nVrjUf6IwidsAB7mHusCVbN8GlIbwNGO8AroCEuotGmXJyfB5Pi2OrTpXVLB0vTVEp8Ft+42wLYJizSjEV8ImpA5L/pAuc0bCc7prg+AxuK/Lvn6MQ06SbcVpO5aYbI7zO3zShi+1sFeZzUrBLJieideVwQy9UEg==";
//		String messageDe = rsaUtil.decipher(res, privateKey);
//		System.out.println("还原后的字符串为:" + message.equals(messageDe));
		
//		AESUtil aesUtil = new AESUtil();
		
//		String q = "{\"sortName\":\"bids.reply_time\",\"deptHisId\":\"10802012\",\"sort\":\"DESC\",\"uniqueId\":\"mini441948392923111424\",\"inquiryType\":\"2\",\"offset\":0,\"limit\":20}";
//		String q1 =   //"qjyx392322692762300000000370";
//		"{\r\n" + 
//		"	\"cardId\": 96,\r\n" + 
//		"	\"uniqueId\": \"qjyx391920638457585665\"\r\n" + 
//		"}";
//		System.out.println(m);
// 		String encrypt = aesUtil.aesEncrypt("123", SystemConstantUtil.AES_KEYWORD);
//         System.out.println("加密后：" + encrypt);
//         String decrypt = aesUtil.aesDecrypt(encrypt, SystemConstantUtil.AES_KEYWORD);
//         System.out.println("解密后：" + decrypt);
	 
	}

}
